
/**----------------string--------------------*/
/**
 * 字符串扩展方法
 * 包含：去空格、只留下数字、判空、判数字、获取长度等。
 */
var StringExt = function () {

	/**
	 * 去掉字符串前后空格
	 */
  function trim (str) {
    return str.replace(/(^\s*)|(\s*$)/g, "");
  }

	/**
	 * 去掉字符串左空格
	 */
  function leftTrim (str) {
    return str.replace(/(^\s*)/g, "");
  }

	/**
	 * 去掉字符串右空格
	 */
  function rightTrim (str) {
    return str.replace(/(\s*$)/g, "");
  }

	/**
	 * 只留下数字
	 */
  function toNumber (str) {
    return str.replace(/\D/g, "");
  }

	/**
	 * 判断字符串是否为空
	 * 
	 * @returns {Boolean}
	 */
  function isEmpty (str) {
    if (!str) {
      return true;
    }
    if (str.trim() == "") {
      return true;
    }
    return false;
  }

	/**
	 * 判断字符串是否为数字
	 * 
	 * @returns {Boolean}
	 */
  function isNumber (str) {
    if (!str) {
      return true;
    }
    var reg = /^[1-9]([0-9\.]+\d)?$/;
    if (reg.test(str)) {
      return true;
    }
    return false;
  }

	/**
	 * 获取字符串长度，汉字算两个字符。
	 */
  function realLength (str) {
    if (!str) {
      return 0;
    }
    var arr = str.match(/[^\x00-\xff]/ig);
    return str.length + (arr == null ? 0 : arr.length);
  }

	/**
	 * 截取字符串
	 * @param str 原始字符串
	 * @param size 截取长度
	 * @param suffix 截取部分替换字符串
	 */
  function truncate (str, size, suffix) {
    size = Number(size || 10);
    suffix = suffix || '...';
    var length = realLength(str);
    if (length < size) {
      return str;
    }
    var resultStr = str.substring(0, size);
    while (realLength(resultStr) > size) {
      resultStr = resultStr.substring(0, resultStr.length - 1);
    }
    return resultStr + suffix;
  }

  return {
    trim: trim,
    leftTrim: leftTrim,
    rightTrim: rightTrim,
    toNumber: toNumber,
    isEmpty: isEmpty,
    isNumber: isNumber,
    realLength: realLength,
    truncate: truncate
  };

}();

/**
 * 去掉前后空格
 */
String.prototype.trim = function () {
  return StringExt.trim(this);
}
/**
 * 去掉左空格
 */
String.prototype.leftTrim = function () {
  return StringExt.leftTrim(this);
}

/**
 * 去掉右空格
 */
String.prototype.rightTrim = function () {
  return StringExt.rightTrim(this);
}
/**
 * 只留下数字
 */
String.prototype.toNumber = function () {
  return StringExt.toNumber(this);
}
/**
 * 判断字符串是否为空
 * @returns {Boolean}
 */
String.prototype.isEmpty = function () {
  return StringExt.isEmpty(this);
}
/**
 * 判断字符串是否为数字
 * @returns {Boolean}
 */
String.prototype.isNumber = function () {
  return StringExt.isNumber(this);
}

/**
 * 获取字符串长度，汉字算两个字符。
 */
String.prototype.realLength = function () {
  return StringExt.realLength(this);
}

/**
 * 截取字符串
 */
String.prototype.truncate = function (size, suffix) {
  return StringExt.truncate(this, size, suffix);
}


/**----------------number--------------------*/

/**
 * Number扩展方法
 * 重新实现了四则运算，解决丢失精度问题。
 */
var NumberExt = function () {

  /**
   * 判断obj是否为一个整数
   */
  function isInteger (obj) {
    return Math.floor(obj) === obj;
  }

  /**
   * 将一个浮点数转成整数，返回整数和倍数。
   * @param floatNum 小数
   * @return 浮点数信息{times:倍数,num:数字}
   */
  function toInteger (floatNum) {
    var ret = { times: 1, num: 0 };
    if (isInteger(floatNum)) {
      ret.num = floatNum;
      return ret;
    }
    var strfi = scientificToNumber(floatNum);
    var dotPos = strfi.indexOf('.');
    var len = 0;
    if (dotPos != -1) {
      len = strfi.substr(dotPos + 1).length;
    }
    var times = Math.pow(10, len);
    var intNum = parseInt(floatNum * times + (floatNum > 0 ? 0.5 : -0.5), 10);
    ret.times = times;
    ret.num = intNum;
    return ret;
  }

  function scientificToNumber (num) {
    if (/\d+\.?\d*e[\+\-]*\d+/i.test(num)) {// 正则匹配科学计数法的数字
      var zero = '0', //
        parts = String(num).toLowerCase().split('e'), // 拆分成系数和指数
        e = parts.pop(), // 存储指数
        l = Math.abs(e), // 取绝对值，l-1就是0的个数
        sign = e / l,          // 判断正负
        coeff_array = parts[0].split('.'); // 将系数按照小数点拆分
      if (sign === -1) { // 如果是小数
        num = zero + '.' + new Array(l).join(zero) + coeff_array.join(''); // 拼接字符串，如果是小数，拼接0和小数点
      } else {
        var dec = coeff_array[1];
        if (dec) {
          l = l - dec.length; // 如果是整数，将整数除第一位之外的非零数字计入位数，相应的减少0的个数
        }
        num = coeff_array.join('') + new Array(l + 1).join(zero); // 拼接字符串，如果是整数，不需要拼接小数点
      }
    }
    return num + '';
  }

  /**
   * 核心方法，实现加减乘除运算，确保不丢失精度
   * 思路：把小数放大为整数（乘），进行算术运算，再缩小为小数（除）
   * @param a 运算数1
   * @param b 运算数2
   * @param digits 精度，保留的小数点数，比如 2, 即保留为两位小数
   * @param op 运算类型，有加减乘除（add/sub/mul/div）
   *
   */
  function calc (a, b, digits, op) {
    var o1 = toInteger(a);
    var o2 = toInteger(b);
    var n1 = o1.num;
    var n2 = o2.num;
    var t1 = o1.times;
    var t2 = o2.times;
    var max = t1 > t2 ? t1 : t2;
    var result = null;
    switch (op) {
      case 'add':
        if (t1 === t2) { // 两个小数位数相同
          result = n1 + n2;
        } else if (t1 > t2) { // o1 小数位 大于 o2
          result = n1 + n2 * (t1 / t2);
        } else { // o1 小数位 小于 o2
          result = n1 * (t2 / t1) + n2;
        }
        result = result / max;
        break;
      case 'sub':
        if (t1 === t2) {
          result = n1 - n2;
        } else if (t1 > t2) {
          result = n1 - n2 * (t1 / t2);
        } else {
          result = n1 * (t2 / t1) - n2;
        }
        result = result / max;
        break;
      case 'mul':
        result = (n1 * n2) / (t1 * t2);
        break;
      case 'div':
        result = (n1 / n2) * (t2 / t1);
        break;
    }
    if (digits !== undefined) {
      result = Number(setScale(result, digits));
    }
    return result;
  }

  /**
   * 加法
   * @param a 数值a
   * @param b 数值b
   * @param 精度
   */
  function add (a, b, digits) {
    return calc(a, b, digits, 'add');
  }

  /**
   * 减法
   * @param a 数值a
   * @param b 数值b
   * @param 精度
   */
  function subtract (a, b, digits) {
    return calc(a, b, digits, 'sub');
  }

  /**
   * 乘法
   * @param a 数值a
   * @param b 数值b
   * @param 精度
   */
  function multiply (a, b, digits) {
    return calc(a, b, digits, 'mul');
  }

  /**
   * 除法
   * @param a 数值a
   * @param b 数值b
   * @param 精度
   */
  function divide (a, b, digits) {
    return calc(a, b, digits, 'div');
  }

  /**
   * 设置数值的精度
   * @param num 数值
   * @param digits 精度
   */
  function setScale (num, digits) {
    if (num == undefined || num == "undefined") {
      num = "0";
    }
    if (digits == undefined || digits == "undefined") {
      digits = 2;
    }
    if (num.toString().indexOf("e") >= 0) {
      //适用任何数字20191110--wanglaibing
      result = Number(num).toFixed(digits);
    } else {
      //对于科学计数法为错误
      var times = Math.pow(10, digits);
      var des = num * times + (num > 0 ? 0.5 : -0.5);
      des = parseInt(des, 10) / times;
      var result = des.toString();
      var dotPos = result.indexOf('.');
      var count;
      if (dotPos < 0) {
        count = digits;
        if (digits > 0) {
          result += '.';
        }
      } else {
        count = result.indexOf('.') + 1 + digits - result.length;
      }
      while (count > 0) {
        result += '0';
        count--;
      }
    }
    return result;
  }

  /**
   * 数字转货币格式（number to currency）
   * @param number 数值
   * @param digits 精度
   * @returns 货币格式字符串
   */
  function n2c (number, digits, zeroVal) {
    if (zeroVal !== undefined && number == 0) {
      return zeroVal;
    }
    if (!number) {
      number = '0.00';
    }
    if (digits) {
      number = Number(number).toFixed(digits);
    }
    var ite = '';
    var dec = '';
    number = String(number);
    if (number.indexOf('.') == -1) {
      ite = number;
    } else {
      var arr = number.split('.');
      ite = arr[0];
      dec = arr[1];
    }
    var re = /(-?\d+)(\d{3})/;
    while (re.test(ite)) {
      ite = ite.replace(re, '$1,$2');
    }
    return ite + '.' + dec;
  }

  /**
   * 货币格式转为数字（currency to number）
   * @param currency 货币格式字符串，例：12,345,678.231
   * @param digits 精度
   * @returns 数值
   */
  function c2n (currency, digits) {
    if (!currency) {
      currency = '0.00';
    }
    var number = currency.replace(' ', '').replace(/,/g, '');
    if (digits) {
      return setScale(number, digits);
    }
    return Number(number);
  }

  function n2dx (number) {
    if (!/^(0|\-*\d*)(\.\d+)?$/.test(number)) {
      return "数据非法";
    }
    var unit = "仟佰拾亿仟佰拾万仟佰拾元角分",
      str = (number.toString().indexOf("-") == 0 ? "负" : "");
    number = (number.toString().indexOf("-") == 0 ? number.toString().substring(1, number.toString().length) : number) + "00";
    var p = number.indexOf('.');
    if (p >= 0) {
      number = number.substring(0, p) + number.substr(p + 1, 2);
    }
    unit = unit.substr(unit.length - number.length);
    for (var i = 0; i < number.length; i++) {
      str += '零壹贰叁肆伍陆柒捌玖'.charAt(number.charAt(i)) + unit.charAt(i);
    }
    str = str.replace(/零(仟|佰|拾|角)/g, "零").replace(/(零)+/g, "零").replace(/零(万|亿|元)/g, "$1").replace(/(亿)万|壹(拾)/g, "$1$2").replace(/^元零?|零分/g, "").replace(/元$/g, "元整");
    if (number == 0) {
      return "零元";
    }
    return str;
  }

  function toPercent (number) {
    if (number === undefined) {
      return '';
    }
    return multiply(number, 100);
  }

  return {
    add: add,
    subtract: subtract,
    multiply: multiply,
    divide: divide,
    setScale: setScale,
    n2c: n2c,
    c2n: c2n,
    n2dx: n2dx,
    toPercent: toPercent
  }
}();

/**
 * 加法
 */
Number.prototype.add = function (num, digits) {
  return NumberExt.add(this, num, digits);
}

/**
 * 减法
 */
Number.prototype.subtract = function (num, digits) {
  return NumberExt.subtract(this, num, digits);
}

/**
 * 乘法
 */
Number.prototype.multiply = function (num, digits) {
  return NumberExt.multiply(this, num, digits);
}

/**
 * 除法
 */
Number.prototype.divide = function (num, digits) {
  return NumberExt.divide(this, num, digits);
}

/**
 * 设置精度、保留<digits>位小数
 */
Number.prototype.setScale = function (digits) {
  return NumberExt.setScale(this, digits);
}

/**
 * 数字转货币格式
 * @param digits 精度
 */
Number.prototype.n2c = function (digits, zeroVal) {
  return NumberExt.n2c(this, digits, zeroVal);
}

/**
 * 货币格式转为数字
 * @param digits 精度
 */
String.prototype.c2n = function (digits) {
  return NumberExt.c2n(this, digits);
}

Number.prototype.n2dx = function () {
  return NumberExt.n2dx(this);
}

Number.prototype.toPercent = function () {
  return NumberExt.toPercent(this);
}

/**----------------date--------------------*/

/**
 * 时间扩展方法
 * 包含：时间格式化、字符串解析为时间、时间对比等
 */
var DateExt = function () {

  /** 毫秒转秒比例 */
  var P_MILLI = 1000;
  /** 秒转分、分转时比例 */
  var P_SEC_MIN_HOUR = 60;
  /** 时转天比例 */
  var P_DAY = 24;

  /** 余数处理方式 - 进1 */
  var ROUND_MODE_UP = 'up';
  /** 余数处理方式 - 舍去 */
  var ROUND_MODE_DOWN = 'down';
  /** 余数处理方式 - 过半进1 */
  var ROUND_MODE_HALF_UP = 'half_up';

  /** 占位符 ph:占位符，digit:位数*/
  var parts = {
    year: { ph: 'yyyy', digit: 4 },
    month: { ph: 'MM', digit: 2 },
    day: { ph: 'dd', digit: 2 },
    hour24: { ph: 'HH', digit: 2 },
    hour12: { ph: 'hh', digit: 2 },
    minute: { ph: 'mm', digit: 2 },
    second: { ph: 'ss', digit: 2 },
    milliSecond: { ph: 'SSS', digit: 3 }
  }
  /** 默认日期格式 */
  var defaultPattern = 'yyyy-MM-dd HH:mm:ss';

	/**
	 * 日期解析
	 * @param dateString 日期字符串
	 * @param pattern 日期格式
	 */
  function parse (dateString, pattern) {
    if (!dateString) {
      return null;
    }
    pattern = pattern || defaultPattern;
    if (dateString.length != pattern.length) {
      throw 'Gived dateString do not match the pattern.';
    }
    var year = getDatePart(dateString, pattern, parts.year.ph);
    var month = getDatePart(dateString, pattern, parts.month.ph);
    var day = getDatePart(dateString, pattern, parts.day.ph);
    var hour = getDatePart(dateString, pattern, parts.hour24.ph);
    var min = getDatePart(dateString, pattern, parts.minute.ph);
    var second = getDatePart(dateString, pattern, parts.second.ph);
    var milliSecond = getDatePart(dateString, pattern, parts.milliSecond.ph);
    var date = new Date();
    if (year) {
      date.setFullYear(year);
    }
    date.setMonth(month ? (month - 1) : 1);
    date.setDate(day || 1);
    date.setHours(hour || 0);
    date.setMinutes(min || 0);
    date.setSeconds(second || 0);
    date.setMilliseconds(milliSecond || 0);
    return date;
  }

	/**
	 * 日期格式化
	 * @param date 日期
	 * @param date 日期格式
	 */
  function format (date, pattern) {
    pattern = pattern || defaultPattern;
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();
    var hour = date.getHours();
    var min = date.getMinutes();
    var second = date.getSeconds();
    var milliSecond = date.getMilliseconds();
    var dateString = pattern.replace(parts.year.ph, fillPlace(year, parts.year.digit, 0));
    dateString = dateString.replace(parts.month.ph, fillPlace(month, parts.month.digit, 0));
    dateString = dateString.replace(parts.day.ph, fillPlace(day, parts.day.digit, 0));
    dateString = dateString.replace(parts.hour24.ph, fillPlace(hour, parts.hour24.digit, 0));
    dateString = dateString.replace(parts.hour12.ph, fillPlace(hour % 12, parts.hour12.digit, 0));
    dateString = dateString.replace(parts.minute.ph, fillPlace(min, parts.minute.digit, 0));
    dateString = dateString.replace(parts.second.ph, fillPlace(second, parts.second.digit, 0));
    dateString = dateString.replace(parts.milliSecond.ph, fillPlace(milliSecond, parts.milliSecond.digit, 0));
    return dateString;
  }

	/**
	 * 计算两个时间的相差时间
	 * @param startDate 开始时间
	 * @param endDate 结束时间
	 * @param mode 返回时间的类别（天、小时、分、秒、毫秒）
	 * @param roundMode 余数处理方式（up:有余数则进1，down:舍去余数，half_up：余数大于一半时进1，否则舍去）
	 * @returns 若传入指定mode（dd、HH、mm、ss、SSS）则返回相差时间按指定mode换算过的值
	 * 			，否则返回相差时间对象，格式为:
	 * 		{
	 *			d : day,
	 *			h : hour,
	 *			m : min,
	 *			s : second,
	 *			ms : milliSecond
	 *		}
	 */
  function dateDiff (startDate, endDate, mode, roundMode) {
    roundMode = roundMode || ROUND_MODE_UP;
    var startTime = startDate.getTime();
    var endTime = endDate.getTime();
    var milliSecond = endTime - startTime;
    if (mode == parts.milliSecond.ph) {
      return milliSecond;
    }
    var second = parseInt(milliSecond / P_MILLI);
    milliSecond = milliSecond % P_MILLI;
    if (mode == parts.second.ph) {
      return second + dealBelong(milliSecond, P_MILLI, roundMode);
    }
    var min = parseInt(second / P_SEC_MIN_HOUR);
    second = second % P_SEC_MIN_HOUR;
    if (mode == parts.minute.ph) {
      return min + dealBelong(second, P_SEC_MIN_HOUR, roundMode);
    }
    var hour = parseInt(min / P_SEC_MIN_HOUR);
    min = min % P_SEC_MIN_HOUR;
    if (mode == parts.hour24.ph) {
      return hour + dealBelong(min, P_SEC_MIN_HOUR, roundMode);
    }
    var day = parseInt(hour / P_DAY);
    hour = hour % P_DAY;
    if (mode == parts.day.ph) {
      return day + dealBelong(hour, P_DAY, roundMode);
    }
    return {
      d: day,
      h: hour,
      m: min,
      s: second,
      ms: milliSecond
    };
  }

	/**
	 * 比较两时间的大小
	 * @param date1 时间1
	 * @param date2 时间2
	 * @returns date2比date1大的毫秒数
	 */
  function compare (date1, date2) {
    return DateExt.dateDiff(date1, date2, 'SSS', null);
  }

	/**
	 * 处理余数
	 * @param belong 余数
	 * @param p 进制
	 * @param roundMode 处理模式
	 */
  function dealBelong (belong, p, roundMode) {
    switch (roundMode) {
      case ROUND_MODE_UP:
        if (belong > 0) {
          return 1
        } else {
          return 0;
        }
      case ROUND_MODE_DOWN:
        return 0;
      case ROUND_MODE_HALF_UP:
        if ((belong / p) >= 0.5) {
          return 1;
        } else {
          return 0;
        }
      default:
        return 0;
    }
  }

	/**
	 * 获取时间字符串中各部分的值
	 * @param dateString 时间字符串
	 * @param pattern 时间格式
	 * @param ph 占位符
	 */
  function getDatePart (dateString, pattern, ph) {
    var index = pattern.indexOf(ph);
    return dateString.substr(index, ph.length);
  }

	/**
	 * 补齐时间部分
	 * @param src 要补位的部分
	 * @param digit 位数
	 * @param char 补位字符
	 */
  function fillPlace (src, digit, char) {
    char = char.toString();
    var less = digit - src.toString().length;
    while (less > 0) {
      src = char + src;
      less--;
    }
    return src;
  }

  return {
    parse: parse,
    format: format,
    dateDiff: dateDiff,
    compare: compare
  };

}();

/**
 * 按指定格式转换为字符串
 * @param pattern 格式 
 *		year : yyyy
 *		month : MM
 *		day : dd
 *		hour24 : HH
 *		hour12 : hh
 *		minute : mm
 *		second : ss
 *		milliSecond : SSS
 * @returns 格式化后的时间字符串 
 */
Date.prototype.toFormatString = function (pattern) {
  return DateExt.format(this, pattern);
}

/**
 * 计算时间差（天）
 * @param when 对比时间
 * @param roundMode 余数处理方式
 * @returns 相差天数
 */
Date.prototype.diffDay = function (when, roundMode) {
  return DateExt.dateDiff(when, this, 'dd', roundMode);
}

/**
 * 计算时间差（小时）
 * @param when 对比时间
 * @param roundMode 余数处理方式
 * @returns 相差小时
 */
Date.prototype.diffHour = function (when, roundMode) {
  return DateExt.dateDiff(when, this, 'HH', roundMode);
}

/**
 * 计算时间差（分钟）
 * @param when 对比时间
 * @param roundMode 余数处理方式
 * @returns 相差分钟
 */
Date.prototype.diffMinute = function (when, roundMode) {
  return DateExt.dateDiff(when, this, 'mm', roundMode);
}

/**
 * 计算时间差（秒）
 * @param when 对比时间
 * @param roundMode 余数处理方式
 * @returns 相差秒
 */
Date.prototype.diffSecond = function (when, roundMode) {
  return DateExt.dateDiff(when, this, 'ss', roundMode);
}

/**
 * 计算时间差
 * @param when 对比时间
 * @param roundMode 余数处理方式
 * @returns 相差时间，格式：
 * 		{
 * 			day : day,
 *			hour : hour,
 *			minute : min,
 *			second : second,
 *			milliSecond : milliSecond
 *		}
 */
Date.prototype.diffDate = function (when) {
  return DateExt.dateDiff(when, this, null, null);
}

/**
 * 比较时间大小
 * @param when 要比较的时间
 * @returns 比时间when大的毫秒数
 */
Date.prototype.compare = function (when) {
  return DateExt.compare(when, this);
}


/**
 * 比较时间大小
 * @param when 要比较的时间
 * @returns 比时间when大的毫秒数
 */
String.prototype.parseDate = function (pattern) {
  return DateExt.parse(this, pattern);
}

